package itdep.common.context.spring;

import itdep.common.context.User;
import itdep.common.context.UserContext;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/**
 * @author Ivan Khalopik
 * @version $Revision: 4 $ $Date: 2010-06-10 13:13:16 +0000 (Thu, 10 Jun 2010) $
 */
public class SpringUserContextInitializationFilter<U extends User> extends GenericFilterBean {
	private final AuthenticationTrustResolver trustResolver;
	private final UserContext<U> userContext;

	public SpringUserContextInitializationFilter(final UserContext<U> userContext) {
		this(new AuthenticationTrustResolverImpl(), userContext);
	}

	public SpringUserContextInitializationFilter(final AuthenticationTrustResolver trustResolver, final UserContext<U> userContext) {
		this.trustResolver = trustResolver;
		this.userContext = userContext;
	}

	public void doFilter(final ServletRequest request, final ServletResponse response,
						 final FilterChain chain) throws IOException, ServletException {
		userContext.init(getCurrentUser());
		try {
			chain.doFilter(request, response);
		} finally {
			userContext.clear();
		}
	}

	@SuppressWarnings({"unchecked"})
	protected U getCurrentUser() {
		final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication == null || trustResolver.isAnonymous(authentication)) {
			return null;
		}

		final Object principal = authentication.getPrincipal();
		if (principal != null && principal instanceof SpringUser) {
			final SpringUser user = (SpringUser) principal;
			final boolean rememberMe = trustResolver.isRememberMe(authentication);
			final Object details = authentication.getDetails();
			if (details != null && details instanceof WebAuthenticationDetails) {
				final WebAuthenticationDetails d = (WebAuthenticationDetails) details;
				user.setDetails(null, d.getRemoteAddress(), rememberMe);
			} else {
				user.setDetails(null, null, rememberMe);
			}
			return (U) user;
		}

		throw new IllegalStateException("Unsupported authentication principal type " + principal);
	}
}
